package com.nulldev.util.scripting.ScriptAPI.baseJS.legacy.mods.fparse;

import static com.nulldev.util.scripting.ScriptAPI.baseJS.legacy.lexer.interper.BaseJSInterperUtils.*;

import java.io.IOException;
import java.io.StringReader;
import java.util.List;

import com.nulldev.util.JVM.JVM;
import com.nulldev.util.VariableAPI.StringsUtil;
import com.nulldev.util.data.Arrays.arrays.FastLists;
import com.nulldev.util.data.Sets.DataSet;

public class FunctionParse {

	private static String previewNextSyntax(final StringReader sr, final int len) throws IOException {
		final char[] o = new char[len];
		sr.mark(len);
		sr.read(o);
		sr.reset();
		return new String(o);
	}

	private static boolean isValueAnArrayOrObject(final StringReader sr) throws IOException {
		final char[] o = new char[1];
		sr.mark(1);
		sr.read(o);
		sr.reset();
		return o[0] == '[' || o[0] == '{';
	}

	public static enum ValueType {
		UNKNOWN,
		ARRAY,
		OBJECT
	}

	private static ValueType getValueType(final StringReader sr) throws IOException {
		final char[] o = new char[1];
		sr.mark(1);
		sr.read(o);
		sr.reset();
		switch (o[0]) {
			case '[':
				return ValueType.ARRAY;
			case '{':
				return ValueType.OBJECT;
			default:
				return ValueType.UNKNOWN;
		}
	}

	public static String[] parseFunctionIntoOperations(final String payload) {
		try {
			JVM.println(payload);
			final StringReader sr = new StringReader(payload);
			boolean exitLoop = false;
			while (true) {
				final String syntax = previewNextSyntax(sr, 3);
				switch (syntax) {
					case "var": {
						boolean isMore = false;
						while (true) {
							if (!isMore)
								sr.skip(4);
							final String varName = readUntil(sr, '=');
							JVM.println("varName: " + varName);
							final String val;
							if (isMore) {
								if (isValueAnArrayOrObject(sr)) {
									final ValueType type = getValueType(sr);
									switch (type) {
										case ARRAY:
											parseArray(sr);
										case UNKNOWN:
										default:
											val = null;
											throw new UnsupportedOperationException("TODO(FUNCTION_PARSE): Add getValueType support to more objects.");
									}
								} else
									throw new UnsupportedOperationException("TODO(FUNCTION_PARSE): Add isMore support to more objects.");
							} else {
								final DataSet<String, Character> o = readUntilEitherWS(sr, ',', ';');
								val = o.getA();
								if (o.getB() == ',')
									isMore = true;
								else
									isMore = false;
							}
							JVM.println(val);
							// EMIT(OPERATIONS, VARIABLE_DECL, varName, VAL)
							if (isMore) {
								continue;
							} else {
								break;
							}
						}
						break;
					}
					default: {
						exitLoop = true;
						break;
					}
				}
				if (exitLoop)
					break;
			}
			throw new UnsupportedOperationException("unimpl basefparse()");
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	private static void parseArray(final StringReader sr) throws IOException {
		sr.read(); // enter array
		int v;
		final StringBuffer b = new StringBuffer(32);
		final List<Object> items = FastLists.list();
		boolean isInFunction = false, isWithinFunction = false, isWithinForEach = false;
		while ((v = sr.read()) != -1) {
//			JVM.println(b.toString() + "\n (isInFunction: " + isInFunction + ", isWithinFunction: "
//					+ isWithinFunction + ", isWithinForEach: " + isWithinForEach + ")");
			if (v == '(' && b.lastIndexOf("function") != -1)
				isInFunction = true;
			else if (v == '(' && b.lastIndexOf("forEach") != -1 && isWithinFunction)
				isWithinForEach = true;
			else if (v == '}' && b.lastIndexOf(")") != -1 && isWithinForEach)
				isWithinForEach = false;

			if (v == ')' && isInFunction)
				isInFunction = false;
			if (v == '{' && isInFunction)
				isWithinFunction = true;
			else if (v == '}' && isWithinFunction)
				isWithinFunction = false;

			if (v != ',')
				b.append((char) v);
			else if (v == ',' && (isWithinFunction || isInFunction || isWithinForEach))
				b.append((char) v);
			else {
				final String out = b.toString();
				b.setLength(0);
				JVM.println(out);
				if (StringsUtil.isNumber(out))
					items.add(StringsUtil.asNumber(out));
			}
		}
	}
}
